package com.xiguaji.tiktok.v1.tiktok.music.service;

import com.xiguaji.boot.toolkit.JsonUtil;
import com.xiguaji.tiktok.utils.ESUtil;
import com.xiguaji.tiktok.utils.Ret;
import com.xiguaji.tiktok.utils.jdbc.PageResponse;
import com.xiguaji.tiktok.v1.tiktok.aweme.dto.BloggerAwemeResponse;
import com.xiguaji.tiktok.v1.tiktok.aweme.repository.AwemeExtRepository;
import com.xiguaji.tiktok.v1.tiktok.aweme.repository.AwemeRepository;
import com.xiguaji.tiktok.v1.tiktok.collect.model.MusicCollection;
import com.xiguaji.tiktok.v1.tiktok.collect.repository.AwemeCollectionRepository;
import com.xiguaji.tiktok.v1.tiktok.collect.repository.MusicCollectionRepository;
import com.xiguaji.tiktok.v1.tiktok.music.dto.DistributionRecord;
import com.xiguaji.tiktok.v1.tiktok.music.dto.MusicResponse;
import com.xiguaji.tiktok.v1.tiktok.music.dto.MusicUsedDto;
import com.xiguaji.tiktok.v1.tiktok.music.dto.SearchData;
import com.xiguaji.tiktok.v1.tiktok.music.repository.MusicRepository;
import lombok.val;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
public class MusicService {

    double [] duration = new double[]{15,30,60};

    @Autowired
    MusicRepository musicRepository;

    @Autowired
    RestHighLevelClient client;


    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @Autowired
    AwemeRepository awemeRepository;

    @Autowired
    AwemeCollectionRepository awemeCollectionRepository;

    @Autowired
    MusicCollectionRepository musicCollectionRepository;

    @Value("${tiktok.data.query.latestDays}")
    int latestDays;

    public String getPageEs(SearchData searchData, long userId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ESUtil.MUSIC_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        if(!StringUtils.isEmpty(searchData.getKeyword())){
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(searchData.getKeyword(),"title"));
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(searchData.getKeyword(),"author"));
            boolQueryBuilder.minimumShouldMatch(1);
        }

        if(null != searchData.getCategory()){
            boolQueryBuilder.must(QueryBuilders.nestedQuery("categories",QueryBuilders.matchQuery("categories.id",searchData.getCategory()),ScoreMode.Total));
        }

        if(!StringUtils.isEmpty(searchData.getRegion())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("region",searchData.getRegion()));
        }

        if(null != searchData.getDuration() && searchData.getDuration() > 0){
            int index = searchData.getDuration() -1;
            if(searchData.getDuration() == 1){
                boolQueryBuilder.must(QueryBuilders.rangeQuery("duration").gt(0).lt(duration[index]));
            } else if(searchData.getDuration() > duration.length) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("duration").gt(duration[duration.length-1]));
            }else {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("duration").gt(duration[index-1]).lt(duration[index]));
            }
        }

        if(null != searchData.getOriginal()){
            boolQueryBuilder.must(QueryBuilders.matchQuery("isOriginal",searchData.getOriginal()));
        }

        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.sort("userGrowth", SortOrder.DESC);
        // searchSourceBuilder.sort("userCount", SortOrder.DESC);
        searchSourceBuilder.from((searchData.getPage()-1)*searchData.getPageSize());
        searchSourceBuilder.size(searchData.getPageSize());
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        val value = searchResponse.getHits().getTotalHits().value;
        val hits = searchResponse.getHits().getHits();
        List<MusicResponse> musics = new ArrayList<>();

        for (SearchHit hit : hits) {
            MusicResponse music = JsonUtil.jsonParse(MusicResponse.class,hit.getSourceAsString());
            Optional<MusicCollection> musicCollectionOptional = musicCollectionRepository.findByMidAndUserId(music.getMid(),userId);


            // 图片替换
            if((music.getCoverMedium().indexOf("sign-sg") > 0 && music.getCoverMedium().indexOf("sign-sg") < 30) || (music.getCoverMedium().indexOf("sign-va") > 0 && music.getCoverMedium().indexOf("sign-va") < 30)){
                music.setCoverMedium(music.getCoverMedium().replace("sign-sg", "amd-va").replace("sign-va", "amd-va").replace("webp", "jpeg"));
            }

            /*for (ESVideo topVideo : music.getTopVideos()) {
                if((topVideo.getCover().indexOf("sign-sg") > 0 && topVideo.getCover().indexOf("sign-sg") < 30) || (topVideo.getCover().indexOf("sign-va") > 0 && topVideo.getCover().indexOf("sign-va") < 30)){
                    topVideo.setCover(topVideo.getCover().replace("sign-sg", "amd-va"));
                    topVideo.setCover(topVideo.getCover().replace("sign-va", "amd-va"));
                }
            }*/

            if(musicCollectionOptional.isPresent()){
                music.setFavorite(true);
                music.setCollectId(musicCollectionOptional.get().getId());
            }
            musics.add(music);
        }

        return Ret.pageData(value,musics);
    }

    public MusicResponse getMusicDetail(long mid,long userId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ESUtil.MUSIC_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery("mid",mid));
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);

        if(searchResponse.getHits().getHits().length == 0){
            return null;
        }
        MusicResponse music = JsonUtil.jsonParse(MusicResponse.class,searchResponse.getHits().getHits()[0].getSourceAsString());
        Optional<MusicCollection> musicCollectionOptional = musicCollectionRepository.findByMidAndUserId(music.getMid(),userId);

        if(musicCollectionOptional.isPresent()){
            music.setFavorite(true);
            music.setCollectId(musicCollectionOptional.get().getId());
        }
        return music;
    }




    public PageResponse<BloggerAwemeResponse> getAwemePageByMusic(long musicId, int sortType,int page,int pageSize,Long userId){
        val startDay = LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond();
        val endDay = LocalDateTime.now().minusDays(latestDays).atZone(ZoneId.systemDefault()).toEpochSecond();
        val pageData = awemeRepository.getPage(musicId, AwemeExtRepository.SearchType.MUSIC,
                page, pageSize,
                endDay,startDay, sortType);
        if(userId != null){
            pageData.getList().forEach( a->{
                val awemeCollectionOptional = awemeCollectionRepository.findByAwemeIdAndUserId(a.getAwemeId(), userId);

                if(awemeCollectionOptional.isPresent()){
                    a.setFavorite(true);
                    a.setCollectId(awemeCollectionOptional.get().getId());
                }
            });
        }
        return pageData;
    }

    public List<DistributionRecord> getRegionDistribution(long musicId){
        String data = redisTemplate.opsForValue().get("dis_music_region_"+musicId);
        List<DistributionRecord> distributionRecords;
        if(StringUtils.isEmpty(data)){
            long beforeDateTime = LocalDateTime.now().minusDays(latestDays).atZone(ZoneId.systemDefault()).toEpochSecond();
            List<Map<String,Object>> mapList = musicRepository.getRegionDistribution(musicId,beforeDateTime);
            distributionRecords =  getDistributionRecords(mapList);
            distributionRecords.removeIf( x -> StringUtils.isEmpty(x.getDescription()));
            redisTemplate.opsForValue().set("dis_music_region_"+musicId,JsonUtil.parseJson(distributionRecords),1, TimeUnit.DAYS);
        }else{
            distributionRecords = JsonUtil.jsonParse(List.class,data);
        }
        return distributionRecords;
    }

    public List<DistributionRecord> getIndustryDistribution(long musicId){
        String data = redisTemplate.opsForValue().get("dis_music_industry_"+musicId);
        List<DistributionRecord> distributionRecords;
        if(StringUtils.isEmpty(data)){
            long beforeDateTime = LocalDateTime.now().minusDays(latestDays).atZone(ZoneId.systemDefault()).toEpochSecond();
            List<Map<String,Object>> mapList = musicRepository.getIndustryDistribution(musicId,beforeDateTime);
            distributionRecords =  getDistributionRecords(mapList);
            distributionRecords.removeIf( x -> StringUtils.isEmpty(x.getDescription()));
            redisTemplate.opsForValue().set("dis_music_industry_"+musicId,JsonUtil.parseJson(distributionRecords),1, TimeUnit.DAYS);
        }else {
            distributionRecords = JsonUtil.jsonParse(List.class,data);

        }
        return distributionRecords;
    }

    private List<DistributionRecord> getDistributionRecords(List<Map<String, Object>> mapList) {
        List<DistributionRecord> distributionRecords = new ArrayList<>();
        long sum = mapList.stream().mapToLong(m -> (long) m.get("count")).sum();
        int otherCount = 0;

        for (int i = 0; i < mapList.size(); i++) {
            if(i < 9){
                DistributionRecord distributionRecord = new DistributionRecord();
                distributionRecord.setDescription(mapList.get(i).get("name") == null ? "" : mapList.get(i).get("name").toString());

                BigDecimal bigDecimal = new BigDecimal((long)mapList.get(i).get("count"));
                double percent =bigDecimal.divide(new BigDecimal(sum),3, RoundingMode.FLOOR).multiply(new BigDecimal(100)).setScale(2).doubleValue();
                if(percent <= 0){
                    continue;
                }
                distributionRecord.setPercent(percent);
                distributionRecords.add(distributionRecord);
            }else{
                otherCount += (long)mapList.get(i).get("count");
            }
        }

        if (otherCount > 0) {
            DistributionRecord distributionRecord = new DistributionRecord();
            distributionRecord.setDescription("Others");
            BigDecimal bigDecimal = new BigDecimal(otherCount);
            double percent =bigDecimal.divide(new BigDecimal(sum),3, RoundingMode.FLOOR).multiply(new BigDecimal(100)).setScale(2).doubleValue();
            distributionRecord.setPercent(percent);
            distributionRecords.add(distributionRecord);
        }

        distributionRecords.sort((o1,o2) ->{
            if(o1.getPercent() > o2.getPercent()){
                return -1;
            }else if(o1.getPercent() < o2.getPercent()){
                return 1;
            }
            return 0;
        });
        return distributionRecords;
    }


    // @Cacheable(value = "BLOGGER:AWEME:MUSIC",key = "#uid+'_'+#day+'_'+#size+'")
    public List<MusicUsedDto> analytic(int day, long uid, int size){
        return musicRepository.findByUidAndAwemeCreateDate(uid,LocalDateTime.now().minusDays(day).atZone(ZoneId.systemDefault()).toEpochSecond(),size);
    }
}
